/*
机器输入词：
1.我想用c语言代码，在linux环境运行，实现对nvm的软件仿真操作
2.使用一个bin文件来模拟nvm，需要把bin文件映射到对应运行时地址，除了函数外，直接访问对应地址也等同于访问bin文件对应偏移位置
3.起始地址宏HD_NVM_START=0X02000000,HD_NVM_SIZE=0X2000000,HD_NVM_PAGE_SIZE=0x500
4.需要有函数支持：页擦、页写、页读、半页擦、半页写、半页读、字节擦、字节写、字节读
*/
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

#define HD_NVM_START 0x02000000
#define HD_NVM_SIZE  0x200000
#define HD_NVM_PAGE_SIZE 0x500

void *nvm_map;

int nvm_open() {
    int fd;
    off_t size = HD_NVM_SIZE;

    // 尝试打开文件，如果不存在则创建它
    fd = open("nvm_demo_cos.bin", O_RDWR | O_CREAT, 0666);
    if (fd == -1) {
        perror("open");
        return -1;
    }

    // 将文件大小扩展到HD_NVM_SIZE，并确保文件内容为0xFF
    if (ftruncate(fd, size) == -1) {
        perror("ftruncate");
        close(fd);
        return -1;
    }

    // 创建一个足够大的缓冲区，填充为0xFF
    char *buffer = (char *)malloc(size);
    if (buffer == NULL) {
        perror("malloc");
        close(fd);
        return -1;
    }
    memset(buffer, 0xFF, size);

    // 将缓冲区的内容写入文件
    if (write(fd, buffer, size) != size) {
        perror("write");
        free(buffer);
        close(fd);
        return -1;
    }

    // 释放缓冲区
    free(buffer);

    // 映射文件到内存
    nvm_map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, HD_NVM_START);
    if (nvm_map == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return -1;
    }

    return 0;
}

int nvm_init() {
    int ret;

    // 打开NVM设备文件
    ret = nvm_open();
    if (ret != 0) {
        perror("nvm_open");
        return -1;
    }

    // TODO: 这里可以添加其他初始化代码

    return 0;
}

int nvm_close() {
    if (munmap(nvm_map, HD_NVM_SIZE) == -1) {
        perror("munmap");
        return -1;
    }

    return 0;
}

void nvm_page_write(int page_num, const void *data) {
    char *page_addr = nvm_map + (page_num * HD_NVM_PAGE_SIZE);
    memcpy(page_addr, data, HD_NVM_PAGE_SIZE);
}

void nvm_page_read(int page_num, void *data) {
    char *page_addr = nvm_map + (page_num * HD_NVM_PAGE_SIZE);
    memcpy(data, page_addr, HD_NVM_PAGE_SIZE);
}

void nvm_halfpage_write(int page_num, int halfpage_num, const void *data) {
    char *halfpage_addr = nvm_map + (page_num * HD_NVM_PAGE_SIZE) + (halfpage_num * (HD_NVM_PAGE_SIZE / 2));
    memcpy(halfpage_addr, data, HD_NVM_PAGE_SIZE / 2);
}

void nvm_halfpage_read(int page_num, int halfpage_num, void *data) {
    char *halfpage_addr = nvm_map + (page_num * HD_NVM_PAGE_SIZE) + (halfpage_num * (HD_NVM_PAGE_SIZE / 2));
    memcpy(data, halfpage_addr, HD_NVM_PAGE_SIZE / 2);
}

void nvm_byte_write(int offset, const void *data) {
    memcpy(nvm_map + offset, data, 1);
}

void nvm_byte_read(int offset, void *data) {
    memcpy(data, nvm_map + offset, 1);
}
